/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

****/
package org.activequant.dao.hibernate;

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.activequant.core.domainmodel.InstrumentSpecification;
import org.activequant.core.types.SecurityType;
import org.activequant.dao.ISpecificationDao;
import org.activequant.util.exceptions.DaoException;
import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.springframework.dao.DataAccessException;
import org.springframework.orm.hibernate3.HibernateCallback;


/**
 * SpecificationDao extends DaoSupportBase&lt;InstrumentSpecification&gt; implements ISpecificationDao.<br/>
 * Holds the following inherited variables:
 * <ul>
 * <li>clazz(Class&lt;InstrumentSpecification&gt;)</li>
 * <li>hibernateTemplate(HibernateTemplate)</li>
 * </ul>
 * <br>
 * <b>History:</b><br>
 *  - [25.08.2007] Created (Erik Nijkamp)<br>
 *  - [18.10.2007] Fixing incremental update (Ulrich Staudinger)<br> 
 *  
 *  @author Erik Nijkamp
 *  @author Ulrich Staudinger
 */
public class SpecificationDao extends DaoSupportBase<InstrumentSpecification> implements ISpecificationDao {
	
	Logger log = Logger.getLogger(SpecificationDao.class);
	/**
	 * Constructs a SpecificationDao(extends DaoSupportBase&lt;InstrumentSpecification&gt; implements ISpecificationDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>InstrumentSpecification.class</code>.
	 */
	public SpecificationDao() {
		super(InstrumentSpecification.class);
	}
	/**
	 * Constructs a SpecificationDao(extends DaoSupportBase&lt;InstrumentSpecification&gt; implements ISpecificationDao) whose inherited clazz(Class&lt;T&gt;) is set to <code>InstrumentSpecification.class</code>.<br/>
	 * The given sessionFactory(SessionFactory) is used to set its Hibernate SessionFactory and create/initialize the inherited hibernateTemplate(HibernateTemplate) for it.
	 * @param sessionFactory
	 */
	public SpecificationDao(SessionFactory sessionFactory) {
		super(InstrumentSpecification.class, sessionFactory);
	}
	/**
	 * <strong>1.</strong> Deletes all persistent instances of the {Candle.class, Quote.class, TradeIndication.class, Order.class, Position.class} whose 
	 * instrumentspecification matches one of the given entities(List&lt;InstrumentSpecification&gt;)<br/>
	 * <strong>2.</strong> Deletes the given entities(List&lt;InstrumentSpecification&gt;) instances themselves.
	 */
	@Override
    public void delete(List<InstrumentSpecification> entities) throws DaoException {
		try {		
			HqlQueries.deleteSpecificationEntities(getHibernateTemplate(), entities.toArray(new InstrumentSpecification[] {}));
			getHibernateTemplate().deleteAll(new ArrayList<InstrumentSpecification>(entities));
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}   	
    }
	/**
	 * <strong>1.</strong> Deletes all persistent instances of the {Candle.class, Quote.class, TradeIndication.class, Order.class, Position.class} whose 
	 * instrumentspecification matches any of the existing InstrumentSpecification instances<br/>
	 * <strong>2.</strong> Deletes all InstrumentSpecification instances.
	 */
	@Override @SuppressWarnings("unchecked")
	public void deleteAll() {
		try {
			List<InstrumentSpecification> specs = getHibernateTemplate().loadAll(clazz);
			// hibernate does not support orphan-delete on one-to-one or many-to-one
			HqlQueries.deleteSpecificationEntities(getHibernateTemplate(), specs.toArray(new InstrumentSpecification[] {}));
			getHibernateTemplate().deleteAll(specs);
		} catch (DataAccessException e) {
			throw new DaoException(e);
		}
	}
	/**
	 * Saves/updates and returns the given entity(InstrumentSpecification).<br/>
	 * If there was no id, it looks it up by finding an example.
	 */
//	@Override
//	public InstrumentSpecification update(InstrumentSpecification entity) {
//		if(!entity.hasId()) {
//			InstrumentSpecification storedSpec = super.findByExample(entity);
//			if(storedSpec != null) {
//				entity.setId(storedSpec.getId());
//				return storedSpec;
//			}
//		}
//		return super.update(entity);
//	}
	
	/**
	 * Saves/updates and returns the given entity(InstrumentSpecification).<br/>
	 * If there was no id, it looks it up by finding an example.
	 */
	@Override
	public InstrumentSpecification update(InstrumentSpecification entity) {
		if(!entity.hasId()) {
			InstrumentSpecification storedSpec = lookupIfNeeded(entity);
			if(storedSpec != null) {
				entity.setId(storedSpec.getId());
				return storedSpec;
			}
		}
		return super.update(entity);
	}
	/**
	 * returns a String for the given spec(InstrumentSpecification) in the form:<br/> 
	 * "from InstrumentSpecification where symbol='${spec.symbol}' and exchange='${spec.exchange}' and currency='${spec.currency}' and securityType='${spec.securityType} ....
	 * @param spec
	 * @return
	 */
	public static String createInstrumentSpecificationLookupQuery(InstrumentSpecification spec){
		StringBuffer queryString=new StringBuffer("from " + InstrumentSpecification.class.getName() + " where symbol='" + spec.getSymbol().toString() + "' and exchange='" + spec.getExchange() + 
				"' and currency='" + spec.getCurrency().toString() + "' and securityType='" + spec.getSecurityType().toString()+"'");
		if(spec.getContractRight()!=null){
			queryString.append(" and contractRight='" + spec.getContractRight()+"'");
		}else{
			queryString.append(" and contractRight is null");
		}
		
		if(spec.getSecurityType()!=SecurityType.STOCK){
			if(spec.getPrimaryExchange()!=null){
				queryString.append(" and primaryExchange='" + spec.getPrimaryExchange()+"'");
			}else{
				queryString.append(" and primaryExchange is null");
			}
		}
		
		if(spec.getExpiry()!=null){
			queryString.append(" and expiry='" + spec.getExpiry().toString()+"'");
		}else{
			queryString.append(" and expiry is null");
		}
		if(spec.getStrike()!=-1){
			queryString.append(" and strike='" + spec.getStrike()+"'");
		}
		return queryString.toString();
	}
	
	@SuppressWarnings("unchecked")
	private InstrumentSpecification lookupIfNeeded(final InstrumentSpecification spec) {
		if(spec.hasId()) return spec; // not needed
		return (InstrumentSpecification)getHibernateTemplate().execute(new HibernateCallback() {
			public Object doInHibernate(Session session) throws HibernateException, SQLException {
				String queryString=createInstrumentSpecificationLookupQuery(spec);
				Query query = session.createQuery(queryString);
				List<InstrumentSpecification> list= query.list();
				if(list.size()==0){
					//log.warn("No instrument specification for example found ...");
					return null; 
				}else if(list.size() > 1) {
					throw new IllegalArgumentException("ambiguity: instrumentSpecification matches more than one database entity: " + spec);
				}
				return list.get(0);
			}
		});
	}
	
	@SuppressWarnings("unchecked")
	@Override
	public InstrumentSpecification findByExample(final InstrumentSpecification spec) {
		return lookupIfNeeded(spec);
	}
}
